/*
Remdator v5.0.0
http://code.google.com/p/remdator/

Copyright (c) 2012 Marc Tanti (www.marctanti.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

;(function ($, undefined) {
if (typeof($.remdator) == "undefined")
{
	$.remdator = {};
}

function DataElement($args)
//Data element storing data which could be loaded remotely using ajax or forms.
//$args: {
//	initLoaded: Whether or not to consider the element's value to be loaded upon creation.
//  defaultAjaxOperation: { Default operation parameters to override missing parameters in ajaxOperations.
//		errorChecker($ajaxOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//			$ajaxOutputArgs: {
//				source: The DataElement object that the operation belongs to.
//				requestData: The jQuery ajax data used to make the ajax request.
//				output: The remote script output.
//			}
//		success($source, $requestData, $output): The function which does something with the output from the remote script if no errors were returned.
//			$ajaxOutputArgs: {
//				source: The DataElement object that the operation belongs to.
//				requestData: The jQuery ajax data used to make the ajax request.
//				output: The remote script output.
//			}
//		settings: Default jQuery ajax settings as specified in http://api.jQuery.com/jQuery.ajax/ (which overrides jQuery defaults) to use when calling a remote script (you cannot override url, data, success or error). If undefined, will use jQuery defaults.
//	}
//	ajaxOperations: { An object where each value, which represents an operation of your choosing, maps to an ajax operation object, creating a new ajax operation.
//		operation name : {
//			url: The url of a remote script to call through ajax for loading or changing remote data.
//			errorChecker($ajaxOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//				$ajaxOutputArgs: {
//					source: The DataElement object that the operation belongs to.
//					requestData: The jQuery ajax data used to make the ajax request.
//					output: The remote script output.
//				}
//			success($source, $requestData, $output): The function which does something with the output from the remote script if no errors were returned.
//				$ajaxOutputArgs: {
//					source: The DataElement object that the operation belongs to.
//					requestData: The jQuery ajax data used to make the ajax request.
//					output: The remote script output.
//				}
//			settings: Custom jQuery ajax settings as specified in http://api.jQuery.com/jQuery.ajax/ (which extends and overrides defaultAjaxOperation) to use when calling the remote script (you cannot override url, data, success or error).
//		}
//	}
//	defaultFormOperation: { Default operation parameters to override missing parameters in formOperations.
//		errorChecker($formOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//			$formOutputArgs: {
//				source: The DataElement object that the operation belongs to.
//				form: The form used to submit the request.
//				output: The remote script output.
//			}
//		success($formOutputArgs): The function which does something with the output from the remote script if no errors were returns.
//			$formOutputArgs: {
//				source: The DataElement object that the operation belongs to.
//				form: The form used to submit the request.
//				output: The remote script output.
//			}
//		settings: { Default settings for submitting a form.
//			form: The jQuery wrapper object of the form to submit. Must be available at the time of calling this function.
//			iframe: The iframe to recieve the script's output in. Must be available at the time of calling this function and must have a unique "name" attribute.
//			dataType: The data type (which overrides defaultFormDataType) of the output of the remote script after submitting the form. Can be either "json", "xml" or "text". Default is "text".
//			preSubmit($preSubmitArgs): The function to call immediately before submitting the form (such as to fill in hidden fields).
//				$preSubmitArgs: {
//					$source: The DataElement object that the operation belongs to.
//					$form: The form that will be used to submit the request.
//				}
//		}
//	}
//	formOperations: { An object where each value, which represents an operation of your choosing, maps to a form operation object, creating a new form submission operation.
//		operation name : {
//			url: The url of the remote script to submit the form to.
//			errorChecker($formOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//				$formOutputArgs: {
//					source: The DataElement object that the operation belongs to.
//					form: The form used to submit the request.
//					output: The remote script output.
//				}
//			success($formOutputArgs): The function which does something with the output from the remote script if no errors were returns.
//				$formOutputArgs: {
//					source: The DataElement object that the operation belongs to.
//					form: The form used to submit the request.
//					output: The remote script output.
//				}
//			settings: { Settings for submitting the form for this operation (which override defaultFormOperation).
//				form: The jQuery wrapper object of the form to submit. Must be available at the time of calling this function.
//				iframe: The iframe to recieve the script's output in. Must be available at the time of calling this function and must have a unique "name" attribute.
//				dataType: The data type of the output of the remote script after submitting the form. Can be either "json", "xml" or "text". Default is "text".
//				preSubmit($preSubmitArgs): The function to call immediately before submitting the form (such as to fill in hidden fields).
//					$preSubmitArgs: {
//						$source: The DataElement object that the operation belongs to.
//						$form: The form that will be used to submit the request.
//					}
//			}
//		}
//	}
//}
{
	if (typeof($args) != "undefined")
	{
		$.remdator.assertArgType({ arg : $args.initLoaded, name : "initLoaded", type : "boolean" });
		$.remdator.assertArgType({ arg : $args.defaultAjaxOperation, name : "defaultAjaxOperation", type : "object", orUndefined : true });
		$.remdator.assertArgType({ arg : $args.defaultFormOperation, name : "defaultFormOperation", type : "object", orUndefined : true });
		$.remdator.assertArgType({ arg : $args.ajaxOperations, name : "ajaxOperations", type : "object", orUndefined : true });
		$.remdator.assertArgType({ arg : $args.formOperations, name : "formOperations", type : "object", orUndefined : true });
		
		var $this = this;
		
		//Whether the DataElement is to be considered as loaded upon creation or not.
		this._loaded = $args.initLoaded;
		
		//The default jQuery ajax settings for ajax operations.
		var $defaultAjaxOperation = {
			errorChecker : function($ajaxOutputArgs){},
			success : function($ajaxOutputArgs){},
			settings : {}
		};
		if (typeof($args.defaultAjaxOperation) != "undefined")
		{
			$.remdator.assertArgType({ arg : $args.defaultAjaxOperation.errorChecker, name : "defaultAjaxOperation.errorChecker", type : "function", orUndefined : true });
			$.remdator.assertArgType({ arg : $args.defaultAjaxOperation.success, name : "defaultAjaxOperation.success", type : "function", orUndefined : true });
			$.remdator.assertArgType({ arg : $args.defaultAjaxOperation.settings, name : "defaultAjaxOperation.settings", type : "object", orUndefined : true });
			
			if (typeof($args.defaultAjaxOperation.errorChecker) != "undefined")
			{
				$defaultAjaxOperation.errorChecker = $args.defaultAjaxOperation.errorChecker;
			}
			
			if (typeof($args.defaultAjaxOperation.success) != "undefined")
			{
				$defaultAjaxOperation.success = $args.defaultAjaxOperation.success;
			}
			
			if (typeof($args.defaultAjaxOperation.settings) != "undefined")
			{
				$defaultAjaxOperation.settings = $args.defaultAjaxOperation.settings;
			}
		}
		
		//The ajax operations.
		this._ajaxOperations = {};
		if (typeof($args.ajaxOperations) != "undefined")
		{
			for (var $name in $args.ajaxOperations)
			{
				$.remdator.assertArgType({ arg : $args.ajaxOperations[$name].url, name : "ajaxOperations."+$name+".url", type : "string" });
				$.remdator.assertArgType({ arg : $args.ajaxOperations[$name].errorChecker, name : "ajaxOperations."+$name+".errorChecker", type : "function", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.ajaxOperations[$name].success, name : "ajaxOperations."+$name+".success", type : "function", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.ajaxOperations[$name].settings, name : "ajaxOperations."+$name+".settings", type : "object", orUndefined : true });
				
				this._ajaxOperations[$name] = $.extend(true, {}, $defaultAjaxOperation);
				
				this._ajaxOperations[$name].url = $args.ajaxOperations[$name].url;
				
				if (typeof($args.ajaxOperations[$name].errorChecker) != "undefined")
				{
					this._ajaxOperations[$name].errorChecker = $args.ajaxOperations[$name].errorChecker;
				}
				
				if (typeof($args.ajaxOperations[$name].success) != "undefined")
				{
					this._ajaxOperations[$name].success = $args.ajaxOperations[$name].success;
				}
				
				if (typeof($args.ajaxOperations[$name].settings) != "undefined")
				{
					this._ajaxOperations[$name].settings = $.extend({}, $defaultAjaxOperation.settings, $args.ajaxOperations[$name].settings);
				}
				
				this._ajaxOperations[$name].lastRequestData = null;
			}
		}
		
		//The default settings for form operations.
		var $defaultFormOperation = {
			errorChecker : function($formOutputArgs){},
			success : function($formOutputArgs){},
			settings : {
				form : undefined,
				iframe : undefined,
				dataType : "text",
				preSubmit : function($preSubmitArgs){}
			}
		};
		if (typeof($args.defaultFormOperation) != "undefined")
		{
			$.remdator.assertArgType({ arg : $args.defaultFormOperation.errorChecker, name : "defaultFormOperation.errorChecker", type : "function", orUndefined : true });
			$.remdator.assertArgType({ arg : $args.defaultFormOperation.success, name : "defaultFormOperation.success", type : "function", orUndefined : true });
			$.remdator.assertArgType({ arg : $args.defaultFormOperation.settings, name : "defaultFormOperation.settings", type : "object", orUndefined : true });
			if (typeof($args.defaultFormOperation.settings) != "undefined")
			{
				$.remdator.assertArgType({ arg : $args.defaultFormOperation.settings.form, name : "defaultFormOperation.settings.form", type : "object", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.defaultFormOperation.settings.iframe, name : "defaultFormOperation.settings.iframe", type : "object", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.defaultFormOperation.settings.dataType, name : "defaultFormOperation.settings.dataType", type : "string", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.defaultFormOperation.settings.preSubmit, name : "defaultFormOperation.settings.preSubmit", type : "function", orUndefined : true });
			}
			
			if (typeof($args.defaultFormOperation.errorChecker) != "undefined")
			{
				$defaultFormOperation.errorChecker = $args.defaultFormOperation.errorChecker;
			}
			
			if (typeof($args.defaultFormOperation.success) != "undefined")
			{
				$defaultFormOperation.success = $args.defaultFormOperation.success;
			}
			
			if (typeof($args.defaultFormOperation.settings) != "undefined")
			{
				if (typeof($args.defaultFormOperation.settings.form) != "undefined")
				{
					if (!$args.defaultFormOperation.settings.form.is("form"))
					{
						throw new $.remdator.GeneralError({ message : "defaultFormOperation.settings.form must be a form object." });
					}
					$defaultFormOperation.settings.form = $args.defaultFormOperation.settings.form;
				}
				
				if (typeof($args.defaultFormOperation.settings.iframe) != "undefined")
				{
					if (!$args.defaultFormOperation.settings.iframe.is("iframe"))
					{
						throw new $.remdator.GeneralError({ message : "defaultFormOperation.settings.iframe must be an iframe object." });
					}
					if ($args.defaultFormOperation.settings.iframe.attr("name") === undefined)
					{
						throw new $.remdator.GeneralError({ message : "defaultFormOperation.settings.iframe must have a unique name attribute." });
					}
					$defaultFormOperation.settings.iframe = $args.defaultFormOperation.settings.iframe;
				}
				
				if (typeof($args.defaultFormOperation.settings.dataType) != "undefined")
				{
					if ($.inArray($args.defaultFormOperation.settings.dataType, ["json", "xml", "text"]) == -1)
					{
						throw new $.remdator.GeneralError({ message : "defaultFormOperation.settings.dataType must be 'json', 'xml' or 'text'." });
					}
					$defaultFormOperation.settings.dataType = $args.defaultFormOperation.settings.dataType;
				}
				
				if (typeof($args.defaultFormOperation.settings.preSubmit) != "undefined")
				{
					$defaultFormOperation.settings.preSubmit = $args.defaultFormOperation.settings.preSubmit;
				}
			}
		}
		
		//The form operations.
		this._formOperations = {};
		if (typeof($args.formOperations) != "undefined")
		{
			for (var $name in $args.formOperations)
			{
				$.remdator.assertArgType({ arg : $args.formOperations[$name].url, name : "formOperations."+$name+".url", type : "string" });
				$.remdator.assertArgType({ arg : $args.formOperations[$name].errorChecker, name : "formOperations."+$name+".errorChecker", type : "function", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.formOperations[$name].success, name : "formOperations."+$name+".success", type : "function", orUndefined : true });
				$.remdator.assertArgType({ arg : $args.formOperations[$name].settings, name : "formOperations."+$name+".settings", type : "object", orUndefined : true });
				
				this._formOperations[$name] = $.extend(true, {}, $defaultFormOperation);
				
				this._formOperations[$name].url = $args.formOperations[$name].url;
				
				if (typeof($args.formOperations[$name].errorChecker) != "undefined")
				{
					this._formOperations[$name].errorChecker = $args.formOperations[$name].errorChecker;
				}
				
				if (typeof($args.formOperations[$name].success) != "undefined")
				{
					this._formOperations[$name].success = $args.formOperations[$name].success;
				}
				
				if (typeof($args.formOperations[$name].settings) != "undefined")
				{
					if (typeof($args.formOperations[$name].settings.form) != "undefined")
					{
						if (!$args.formOperations[$name].settings.form.is("form"))
						{
							throw new $.remdator.GeneralError({ message : "formOperations."+$name+".settings.form must be a form object." });
						}
						this._formOperations[$name].settings.form = $args.formOperations[$name].settings.form;
					}
					
					if (typeof($args.formOperations[$name].settings.iframe) != "undefined")
					{
						if (!$args.formOperations[$name].settings.iframe.is("iframe"))
						{
							throw new $.remdator.GeneralError({ message : "formOperations."+$name+".settings.iframe must be an iframe object." });
						}
						this._formOperations[$name].settings.iframe = $args.formOperations[$name].settings.iframe;
					}
					
					if (typeof($args.formOperations[$name].settings.dataType) != "undefined")
					{
						if ($.inArray($args.formOperations[$name].settings.dataType, ["json", "xml", "text"]) == -1)
						{
							throw new $.remdator.GeneralError({ message : "formOperations."+$name+".settings.dataType must be 'json', 'xml' or 'text'." });
						}
						this._formOperations[$name].settings.dataType = $args.formOperations[$name].settings.dataType;
					}
					
					if (typeof($args.formOperations[$name].settings.preSubmit) != "undefined")
					{
						$.remdator.assertArgType({ arg : $args.formOperations[$name].settings.preSubmit, name : "formOperations."+$name+".settings.preSubmit", type : "function" });
						this._formOperations[$name].settings.preSubmit = $args.formOperations[$name].settings.preSubmit;
					}
				}
				
				if (typeof(this._formOperations[$name].settings.form) == "undefined")
				{
					throw new $.remdator.GeneralError({ message : "formOperations."+$name+".settings.form cannot be undefined if it is also undefined in defaultFormOperation." });
				}
				
				if (typeof(this._formOperations[$name].settings.iframe) == "undefined")
				{
					throw new $.remdator.GeneralError({ message : "formOperations."+$name+".settings.iframe cannot be undefined if it is also undefined in defaultFormOperation." });
				}
			}
		}
		
		//The listeners for when the data element's value has changed.
		this.__changeListeners = new Array();
		
		//The listeners for when the data element starts a remote request.
		this.__remoteLoadingListeners = new Array();
		
		//The listeners for when the data element's remote request completes without error, just before it is about to be locally updated.
		this.__remoteReadyListeners = new Array();
		
		//The listeners for when the data element's remote request results in an error.
		this.__remoteErrorListeners = new Array();
	}
}

/***********************************/
DataElement.prototype.isLoaded = function()
//Check if the data in the element has been loaded.
{
	return this._loaded;
};

/***********************************/
DataElement.prototype.addChangeListener = function($args)
//Add a listener to the data element to observe for when the data element's value has changed.
//$args: {
//	listener($eventArgs): A function which is called when the data element's value has changed.
//		$eventArgs: {
//			source: The DataElement object which fired the event.
//		}
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.listener, name : "listener", type : "function" });
	
	this.__changeListeners.push($args.listener);
};

/***********************************/
DataElement.prototype.fireChangeEvent = function()
//Notify all listeners that the data element's selected value was changed.
{
	var $eventArgs = { source : this };
	for (var $i = 0; $i < this.__changeListeners.length; $i++)
	{
		this.__changeListeners[$i]($eventArgs);
	}
};

/***********************************/
DataElement.prototype.addRemoteLoadingListener = function($args)
//Add a listener to the data element to observe for when the data element starts a remote request.
//$args: {
//	listener($eventArgs): A function which is called when the data element starts a remote request.
//		$eventArgs: {
//			source: The DataElement object which fired the event.
//			operationName: The name of the operation which fired the event.
//		}
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.listener, name : "listener", type : "function" });
	
	this.__remoteLoadingListeners.push($args.listener);
};

/***********************************/
DataElement.prototype.fireRemoteLoadingEvent = function($args)
//Notify all listeners that the data element started a remote request.
//$args: {
//	operationName: The name of the operation which fired the event.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	
	this._loaded = false;
	
	var $eventArgs = { source : this, operationName : $args.operationName };
	for (var $i = 0; $i < this.__remoteLoadingListeners.length; $i++)
	{
		this.__remoteLoadingListeners[$i]($eventArgs);
	}
};

/***********************************/
DataElement.prototype.addRemoteReadyListener = function($args)
//Add a listener to the data element to observe for when the data element's remote request completes without error, just before it is about to be locally updated.
//$args: {
//	listener($eventArgs): A function which is called when the data element's remote request completes without error, just before it is about to be locally updated.
//		$eventArgs: {
//			source: The DataElement object which fired the event.
//			operationName: The name of the operation which fired the event.
//		}
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.listener, name : "listener", type : "function" });
	
	this.__remoteReadyListeners.push($args.listener);
};

/***********************************/
DataElement.prototype.fireRemoteReadyEvent = function($args)
//Notify all listeners that the data element's remote request completed without error, just before it is about to be locally updated.
//$args: {
//	operationName: The name of the operation which fired the event.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	
	this._loaded = true;
	
	var $eventArgs = { source : this, operationName : $args.operationName };
	for (var $i = 0; $i < this.__remoteReadyListeners.length; $i++)
	{
		this.__remoteReadyListeners[$i]($eventArgs);
	}
};

/***********************************/
DataElement.prototype.addRemoteErrorListener = function($args)
//Add a listener to the data element to observe for when the data element's remote request results in an error.
//$args: {
//	listener($eventArgs): A function which is called when the data element's remote request results in an error.
//		$eventArgs: {
//			error: The error that happened as a string.
//			data: The user defined data associated with the error.
//			source: The DataElement object which fired the event.
//			operationName: The name of the operation which fired the event.
//		}
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.listener, name : "listener", type : "function" });
	
	this.__remoteErrorListeners.push($args.listener);
};

/***********************************/
DataElement.prototype.fireRemoteErrorEvent = function($args)
//Notify all listeners that the data element's remote request resulted in an error.
//$args: {
//	error: The error that happened as a string.
//	data: The user defined data associated with the error.
//	operationName: The name of the operation which fired the event.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.error, name : "error", type : "string" });
	$.remdator.assertArgPresent({ arg : $args.data, name : "data" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	
	this._loaded = true;
	
	var $eventArgs = { error : $args.error, data : $args.data, source : this, operationName : $args.operationName };
	for (var $i = 0; $i < this.__remoteErrorListeners.length; $i++)
	{
		this.__remoteErrorListeners[$i]($eventArgs);
	}
};

/***********************************/
DataElement.prototype.getValue = function()
//Get the current value. (Place holder)
{
};

/***********************************/
DataElement.prototype.setValue = function($args)
//Set the current value. (Place holder)
//$args: {
//	value: The value to set.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgPresent({ arg : $args.value, name : "value" });
};

/***********************************/
DataElement.prototype.callAjaxOperation = function($args)
//Call an ajax operation.
//$args: {
//	operationName: The name of the operation (as given in ajaxOperations argument).
//	requestData: The jQuery ajax data parameter to use.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	$.remdator.assertArgType({ arg : $args.requestData, name : "requestData", type : "object" });
	
	if (typeof(this._ajaxOperations[$args.operationName]) == "undefined")
	{
		throw new $.remdator.GeneralError({ message : $args.operationName + " is not an existing operationName." });
	}
	if (this._ajaxOperations[$args.operationName].lastRequestData === null && $args.requestData === null)
	{
		throw new $.remdator.GeneralError({ message : "This is the first time that the ajax operation " + $args.operationName + " has been called so requestData must be specified in the arguments." });
	}
	
	var $operation = this._ajaxOperations[$args.operationName];
	
	if ($args.requestData !== null)
	{
		$operation.lastRequestData = $args.requestData;
	}
	
	this._ajaxOperationTemplate({
		operationName : $args.operationName,
		url : $operation.url,
		errorChecker : $operation.errorChecker,
		success : $operation.success,
		ajaxSettings : $operation.settings,
		requestData : $operation.lastRequestData
	});
}

/***********************************/
DataElement.prototype._ajaxOperationTemplate = function($args)
//Template for making an ajax request in order to change local data with new data from the script.
//$args: {
//	operationName: The name of the operation making the request (for event firing info).
//	url: The url of the remote script.
//	errorChecker($ajaxOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//		$ajaxOutputArgs: {
//			source: The DataElement object that the operation belongs to.
//			requestData: The jQuery ajax data used to make the ajax request.
//			output: The remote script output.
//		}
//	success($ajaxOutputArgs): The function which does something with the output from the remote script after checking that no errors were returned.
//		$ajaxOutputArgs: {
//			source: The DataElement object that the operation belongs to.
//			requestData: The jQuery ajax data used to make the ajax request.
//			output: The remote script output.
//		}
//	ajaxSettings: The jQuery ajax extra settings to use when calling the remote script.
//	requestData: The jQuery ajax data parameter to use.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.url, name : "url", type : "string" });
	$.remdator.assertArgType({ arg : $args.ajaxSettings, name : "ajaxSettings", type : "object" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	$.remdator.assertArgType({ arg : $args.requestData, name : "requestData", type : "object" });
	$.remdator.assertArgType({ arg : $args.errorChecker, name : "errorChecker", type : "function" });
	$.remdator.assertArgType({ arg : $args.success, name : "success", type : "function" });
	
	var $this = this;
	
	this.fireRemoteLoadingEvent({ operationName : $args.operationName });
	
	$.ajax($.extend(
		{},
		$args.ajaxSettings,
		{
			url: $args.url,
			data: $args.requestData,
			success: function($output)
			{
				try
				{
					$args.errorChecker({ source : $this, requestData : $args.requestData, output : $output });
					
					$this.fireRemoteReadyEvent({ operationName : $args.operationName });
					
					$args.success({ source : $this, requestData : $args.requestData, output : $output });
				}
				catch($e)
				{
					if ($e instanceof $.remdator.RemoteError)
					{
						$this.fireRemoteErrorEvent({ error : $e.message, data : $e.data, operationName : $args.operationName });
					}
					else
					{
						throw $e;
					}
				}
			},
			error: function($jqXHR, $textStatus, $errorThrown)
			{
				$this.fireRemoteErrorEvent({ error : $textStatus, data : { jqXHR : $jqXHR, errorThrown : $errorThrown }, operationName : $args.operationName });
			}
		}
	));
};

/***********************************/
DataElement.prototype.callFormOperation = function($args)
//Call a form operation.
//$args: {
//	operationName: The name of the operation (as given in formOperations argument).
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	
	if (typeof(this._formOperations[$args.operationName]) == "undefined")
	{
		throw new $.remdator.GeneralError({ message : $args.operationName + " is not an existing operationName." });
	}
	
	var $operation = this._formOperations[$args.operationName];
	this._formOperationTemplate({
		operationName : $args.operationName,
		url : $operation.url,
		errorChecker : $operation.errorChecker,
		success : $operation.success,
		form : $operation.settings.form,
		iframe : $operation.settings.iframe,
		dataType : $operation.settings.dataType,
		preSubmit : $operation.settings.preSubmit
	});
}

/***********************************/
DataElement.prototype.__iframeLoadListener = function($args)
//Listener for when a script which was requested through a form has returned a response.
//$args: {
//	operationName: The name of the operation making the request (for event firing info).
//	errorChecker($formOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//		$formOutputArgs: {
//			source: The DataElement object that the operation belongs to.
//			form: The form used to submit the request.
//			output: The remote script output.
//		}
//	success($formOutputArgs): The function which does something with the output from the remote script after checking that no errors were returned.
//		$formOutputArgs: {
//			source: The DataElement object that the operation belongs to.
//			form: The form used to submit the request.
//			output: The remote script output.
//		}
//	form: The jQuery wrapper object of the form to submit.
//	iframe: The iframe to recieve the script's output in.
//	dataType: The data type of the remote script's output.
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.form, name : "form", type : "object" });
	$.remdator.assertArgType({ arg : $args.iframe, name : "iframe", type : "object" });
	$.remdator.assertArgType({ arg : $args.dataType, name : "dataType", type : "string" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	$.remdator.assertArgType({ arg : $args.errorChecker, name : "errorChecker", type : "function" });
	$.remdator.assertArgType({ arg : $args.success, name : "success", type : "function" });
	
	try
	{
		var $output = $args.iframe.contents().find("body").html();
		switch ($args.dataType)
		{
			case "json":
				try
				{
					$output = $.parseJSON($output);
				}
				catch ($e)
				{
					this.fireRemoteErrorEvent({ error : $e.name, data : { exception : $e }, operationName : $args.operationName });
				}
				break;
			case "xml":
				try
				{
					$output = $.parseXML($output);
				}
				catch ($e)
				{
					this.fireRemoteErrorEvent({ error : $e.name, data : { exception : $e }, operationName : $args.operationName });
				}
				break;
		}
		
		$args.errorChecker({ source : this, form : $args.form, output : $output });
		
		$this.fireRemoteReadyEvent({ operationName : $args.operationName });
		
		$args.success({ source : this, form : $args.form, output : $output });
		
		$args.iframe.unbind("load.remdator");
	}
	catch($e)
	{
		$args.iframe.unbind("load.remdator");
		
		if ($e instanceof $.remdator.RemoteError)
		{
			this.fireRemoteErrorEvent({ error : $e.message, data : $e.data, operationName : $args.operationName });
		}
		else
		{
			throw $e;
		}
	}
}

/***********************************/
DataElement.prototype._formOperationTemplate = function($args)
//Template for making a form submission in order to change local data with new data from the script.
//$args: {
//	operationName: The name of the operation making the request (for event firing info).
//	url: The url of the remote script to submit the form to.
//	errorChecker($formOutputArgs): The function which checks the output from the remote script to see if it returned an error and throws a RemoteError exception if so.
//		$formOutputArgs: {
//			source: The DataElement object that the operation belongs to.
//			form: The form used to submit the request.
//			output: The remote script output.
//		}
//	success($formOutputArgs): The function which does something with the output from the remote script after checking that no errors were returned.
//		$formOutputArgs: {
//			source: The DataElement object that the operation belongs to.
//			form: The form used to submit the request.
//			output: The remote script output.
//		}
//	form: The jQuery wrapper object of the form to submit.
//	iframe: The iframe to recieve the script's output in.
//	dataType: The data type of the remote script's output.
//	preSubmit($preSubmitArgs): The function to call immediately before submitting the form.
//		$preSubmitArgs: {
//			$source: The DataElement object that the operation belongs to.
//			$form: The form that will be used to submit the request.
//		}
//}
{
	$.remdator.assertArgType({ arg : $args, name : "arguments", type : "object" });
	$.remdator.assertArgType({ arg : $args.url, name : "url", type : "string" });
	$.remdator.assertArgType({ arg : $args.form, name : "form", type : "object" });
	$.remdator.assertArgType({ arg : $args.iframe, name : "iframe", type : "object" });
	$.remdator.assertArgType({ arg : $args.dataType, name : "dataType", type : "string" });
	$.remdator.assertArgType({ arg : $args.operationName, name : "operationName", type : "string" });
	$.remdator.assertArgType({ arg : $args.preSubmit, name : "preSubmit", type : "function" });
	$.remdator.assertArgType({ arg : $args.errorChecker, name : "errorChecker", type : "function" });
	$.remdator.assertArgType({ arg : $args.success, name : "success", type : "function" });
	
	$this = this;
	
	try
	{
		$args.form.attr("action", $args.url);
		$args.form.attr("target", $args.iframe.attr("name"));
		
		$args.iframe.unbind("load.remdator");
		$args.iframe.bind("load.remdator", function() {
			$this.__iframeLoadListener($args);
		});
		
		$args.preSubmit({ source : this, form : $args.form });
		
		this.fireRemoteLoadingEvent({ operationName : $args.operationName });
		
		$args.form.submit();
	}
	catch ($e)
	{
		$args.iframe.unbind("load.remdator");
		throw $e;
	}
};

//Add to remdator namespace in jQuery.
$.remdator.DataElement = DataElement;
})(jQuery);